GX
GX (Flipper/Hollywood GPU) notes from Gecko, see Games for the bugs these caused
Vertex Skip
An indexed position with all bits set means "skip this vertex":
format reserved index meaning
----------------------------------------
8-bit 0xFF skip vertex
16-bit 0xFFFF skip vertex
Only applies to the indexed position field! Other fields use 0xFF according to their own format (e.g. alpha 0xFF = opaque). Documented in Nintendo's US6700586B1 patent.
A skipped record doesn't occupy a corner in the strip, so 0, 1, SKIP, 2, 3 is the same quad as 0, 1, 2, 3.
The bytes still exist though! E.g. 8-bit position index + RGBA color is 5 bytes, skipped or not. The parser must consume the full record (using the current vertex format), otherwise it reads a color byte as the next position index and loses its place:
for each vertex record in the draw:
record = consume the complete record using the current format
if its indexed position is the reserved all ones value:
continue
decode the record and append its vertex
assemble the draw's triangles from the retained vertices
- Filter skipped records before vertex decoding
- Update the vertex count per draw, otherwise a removed record can join one draw's vertices to the next
- Depth plane tracking for z-freeze needs to agree on which triangles exist, so it has to run on the filtered list
Code: filter_skipped_vertices
Test: vertex-skip (direct, 8-bit and 16-bit indices, all 3 should match)
z-freeze
Lets a draw reuse the depth plane of an earlier triangle instead of its own depth. libogc: GX_SetCoPlanar(GX_ENABLE).
- While z-freeze is off, every triangle replaces the saved plane
- While it's on, the last plane is kept and following draws evaluate it for depth (own outline/colors/UVs, borrowed depth)
- The reference triangle doesn't need to write color or depth to establish a plane
Plane from the 3 screen space vertices:
depth(x, y) = A*x + B*y + C
Evaluated for every pixel of the new draw, including pixels outside the original triangle, so the result can leave the 0-1 range (see below). Degenerate (line/point) triangles don't define a plane.
Dolphin's January 2015 progress report also covers this.
Plane Overflow
Clamping the extrapolated depth to max is wrong. Hardware readbacks (GX_ALWAYS write, GX_PeekZ read), horizontal and vertical slopes give the same values:
plane depth Wii Z24 ~
----------------------------------
0.25 0x400000 0.25
1.25 0xFFFFFF 1
4.25 0x000000 0
8.25 0x3FF7E0 0.25
12.25 0x000000 0
16.25 0x3FEFC0 0.25
Looks like a value overflowing every 8 depth units before being clamped, which fits a signed 27-bit intermediate (2^27 / 2^24 = 8, range -4 to <+4):
- 4.25 wraps to -3.75 -> clamped to 0
- 8.25 is a full cycle -> back to 0.25
Note: wrapping with % 1.0 gives narrow stripes instead of the broad bands seen on hardware. 27-bit is just what matches, not proven, and some exact readings still differ.
Code: plane calculation, depth shader w/ overflow
Test: z-freeze (sloped planes), original 2-panel test
Honorary mention: hazelwiss (hardware testing)
TEV Wrapping
TEV results must be reduced to 8 bits by wrapping (keep low byte), not clamping, and not left wide. Unclamped stages keep a signed intermediate of -1024..1023.
Where the wrap happens matters:
- A/B/C inputs read only the low byte of a register
- D input keeps the wide (signed) value
- Final RGB/alpha output keeps the low byte before alpha test / blending
Making every stored register 8 bits loses information too early (breaks D).
- Input wrapping alone fixes Sunshine but not Galaxy (the eye stage creates the overflow itself)
- Output wrapping alone fixes Galaxy and makes Sunshine almost right (off by 2)
Other bits:
- C factor is expanded to
C + (C >> 7)before multiplying so 255 represents exactly 1 - Scaled additions round by adding 128 before
>> 8
Commit 14aae5a: integer arithmetic in the regular combiners, A/B/C inputs wrapped to 8 bits, final RGB/alpha wrapped before testing. Still a mixed implementation (combiner in ints, TEV registers as normalized floats), not sure yet what the implications are.
Dolphin: Pixel Processing Problems